import { Injectable, Logger } from '@nestjs/common'
import { ConfigService } from '@nestjs/config'
import { createClient, RedisClient } from 'redis'

/**
 * Redis服务类
 * 使用node-redis库实现Redis连接和操作
 * 支持集群模式和单机模式
 */
@Injectable()
export class RedisService {
  private readonly logger = new Logger(RedisService.name)
  private client!: RedisClient
  private isCluster: boolean

  constructor(private configService: ConfigService) {
    this.isCluster = this.configService.get('REDIS_CLUSTER') === 'true'
    this.initClient()
  }

  /**
   * 初始化Redis客户端
   */
  private async initClient() {
    try {
      const options = {
        url: `redis://${this.configService.get('REDIS_HOST', '127.0.0.1')}:${this.configService.get(
          'REDIS_PORT',
          '6379'
        )}`,
        password: this.configService.get('REDIS_PASSWORD'),
        database: parseInt(this.configService.get('REDIS_DB', '0')),
      }

      this.client = createClient(options)

      // 设置事件监听
      this.client
        .on('connect', () => this.logger.log('Redis连接成功'))
        .on('ready', () => this.logger.log('Redis准备就绪'))
        .on('error', (err: Error) => this.logger.error(`Redis错误: ${err.message}`))
        .on('reconnecting', () => this.logger.log('Redis正在重连'))
        .on('end', () => this.logger.log('Redis连接关闭'))

      await this.client.connect()
    } catch (err) {
      this.logger.error('Redis初始化失败', err)
      throw new Error('Redis服务初始化失败')
    }
  }

  /**
   * 设置键值对
   * @param key 键
   * @param value 值
   * @param ttl 过期时间（秒）
   * @returns 操作结果
   */
  async set(key: string, value: any, ttl?: number): Promise<string> {
    try {
      const val = typeof value === 'string' ? value : JSON.stringify(value)
      if (ttl) {
        await this.client.set(key, val, { EX: ttl })
        return 'OK'
      }
      await this.client.set(key, val)
      return 'OK'
    } catch (err) {
      this.logger.error(`设置键值对失败: ${key}`, err)
      throw new Error('设置键值对失败')
    }
  }

  /**
   * 获取键值
   * @param key 键
   * @returns 值
   */
  async get<T>(key: string): Promise<T | null> {
    try {
      const value = await this.client.get(key)
      if (!value) return null

      try {
        return JSON.parse(value) as T
      } catch {
        return value as T
      }
    } catch (err) {
      this.logger.error(`获取键值失败: ${key}`, err)
      throw new Error('获取键值失败')
    }
  }

  /**
   * 删除键
   * @param key 键
   * @returns 删除数量
   */
  async del(key: string): Promise<number> {
    try {
      return await this.client.del(key)
    } catch (err) {
      this.logger.error(`删除键失败: ${key}`, err)
      throw new Error('删除键失败')
    }
  }

  /**
   * 设置键过期时间
   * @param key 键
   * @param seconds 过期时间（秒）
   * @returns 操作结果
   */
  async expire(key: string, seconds: number): Promise<boolean> {
    try {
      const result = await this.client.expire(key, seconds)
      return result
    } catch (err) {
      this.logger.error(`设置过期时间失败: ${key}`, err)
      throw new Error('设置过期时间失败')
    }
  }

  /**
   * 获取键剩余过期时间
   * @param key 键
   * @returns 剩余时间（秒）
   */
  async ttl(key: string): Promise<number> {
    try {
      return await this.client.ttl(key)
    } catch (err) {
      this.logger.error(`获取过期时间失败: ${key}`, err)
      throw new Error('获取过期时间失败')
    }
  }

  /**
   * 检查键是否存在
   * @param key 键
   * @returns 是否存在
   */
  async exists(key: string): Promise<boolean> {
    try {
      const result = await this.client.exists(key)
      return result > 0
    } catch (err) {
      this.logger.error(`检查键存在失败: ${key}`, err)
      throw new Error('检查键存在失败')
    }
  }

  /**
   * 关闭Redis连接
   */
  async close(): Promise<void> {
    try {
      await this.client.quit()
    } catch (err) {
      this.logger.error('关闭Redis连接失败', err)
      throw new Error('关闭Redis连接失败')
    }
  }

  /**
   * 健康检查
   * @returns 是否健康
   */
  async healthCheck(): Promise<boolean> {
    try {
      await this.client.ping()
      return true
    } catch (err) {
      this.logger.error('Redis健康检查失败', err)
      return false
    }
  }
}
